package cn.yulr.http;

import org.apache._http.ExceptionLogger;
import org.apache._http.impl.nio.bootstrap.HttpServer;
import org.apache._http.impl.nio.bootstrap.ServerBootstrap;
import org.apache._http.impl.nio.reactor.IOReactorConfig;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 基于 HttpCore 的 简单HttpServer的实现
 * 不支持复杂的交互逻辑
 * 推荐用来支持 ask-request 模式实现
 */
public class MHttpServer {

    private static final String TAG = "MHttpServer";

    public static void log(String log) {
        System.err.println(log);
    }

    private HttpServer httpServer; // Http 服务器对象

    /**
     * 当前 Http 服务器的端口信息
     */
    private int httpServerPort;

    public int getHttpServerPort() {
        return httpServerPort;
    }

    public String getInetAddress() {
        return inetAddress;
    }

    private String httpRequestRoot = "http://";

    public String getHttpRequestRoot() {
        return httpRequestRoot;
    }

    /**
     * 当前服务器绑定的IP地址信息
     */
    private String inetAddress;

    /**
     * writeTo
     * 控制启动和退出流程的信号量
     */
    private final static AtomicBoolean isHttpServerInited = new AtomicBoolean(false);

    private List<MHttpAsyncRequestHandler> httpAsyncRequestHandlerList = new ArrayList<>();

    public boolean add(MHttpAsyncRequestHandler httpAsyncRequestHandler) {
        if (isHttpServerInited()) return false;
        httpAsyncRequestHandlerList.add(httpAsyncRequestHandler);
        return true;
    }

    public boolean isHttpServerInited() {
        synchronized (isHttpServerInited) {
            return isHttpServerInited.get();
        }
    }

    public void initHttpServer(int port, String inetAddress, String httpServerName) {
        int ioThreadCount = 0;
        if (Runtime.getRuntime().availableProcessors() <= 4) {
            ioThreadCount = 1;
        } else {
            ioThreadCount = Runtime.getRuntime().availableProcessors() / 4;
        }
        initHttpServer(port, inetAddress, httpServerName, 0, true,
                ioThreadCount, 0);
    }

    /**
     * 初始化并启动 Http 服务器
     *
     * @param port           启动服务器时使用的端口号信息
     * @param inetAddress    Http 服务器绑定的IP地址信息
     * @param httpServerName 服务器名称
     * @param timeOut        请求超时时间
     * @param tcpNoDelay     是否立即发送数据
     */
    public void initHttpServer(int port, String inetAddress, String httpServerName, int timeOut,
                               boolean tcpNoDelay, int ioThreadCount, int connectTimeOut) {
        synchronized (isHttpServerInited) {
            if (!isHttpServerInited.get()) {
                this.httpServerPort = port;
                this.inetAddress = inetAddress;
                if (port > 0 && port < 65536 && inetAddress != null) {
                    httpRequestRoot += inetAddress;
                    httpRequestRoot += ":";
                    httpRequestRoot += port;
                    httpRequestRoot += "/";
                    log("当前 可访问的 Http 连接为：" + httpRequestRoot);
                    InetAddress address = null;
                    try {
                        address = InetAddress.getByName(inetAddress);
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }

                    if (address == null) return;

                    IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
                            /** 超时10分钟 */
                            .setSoTimeout(timeOut)
                            .setIoThreadCount(ioThreadCount)
                            .setConnectTimeout(connectTimeOut)
                            /** 立即发送数据 */
                            .setTcpNoDelay(tcpNoDelay)
                            .build();

                    ServerBootstrap serverBootstrap = ServerBootstrap.bootstrap()
                            .setListenerPort(port)
                            .setIOReactorConfig(ioReactorConfig)
                            .setLocalAddress(address)
                            .setServerInfo(httpServerName)
                            .setExceptionLogger(ExceptionLogger.STD_ERR);

                    for (MHttpAsyncRequestHandler httpAsyncRequestHandler : httpAsyncRequestHandlerList) {
                        if (httpAsyncRequestHandler.getRequestAction() != null && !"".equals(httpAsyncRequestHandler.getRequestAction())) {
                            String requestAction = "";
                            if (!httpAsyncRequestHandler.getRequestAction().startsWith("/")) {
                                requestAction = "/" + httpAsyncRequestHandler.getRequestAction();
                            } else {
                                requestAction = httpAsyncRequestHandler.getRequestAction();
                            }
                            serverBootstrap = serverBootstrap.registerHandler(requestAction, httpAsyncRequestHandler);
                        }
                    }

                    httpServer = serverBootstrap.create();
                    try {
                        httpServer.start();
                        httpAsyncRequestHandlerList.clear();
                        httpAsyncRequestHandlerList = null;
                        isHttpServerInited.set(true);
                        log("Http服务器初始化成功");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public void releaseHttpServer() {
        synchronized (isHttpServerInited) {
            if (isHttpServerInited.get()) {
                httpServer.shutdown(5, TimeUnit.SECONDS);
                httpServer = null;
                isHttpServerInited.set(false);
            } else {
                log("Http服务器尚未启动，执行释放服务器资源失败");
            }
        }
    }

}
